home *** CD-ROM | disk | FTP | other *** search
/ Freelog 115 / FreelogNo115-MaiJuin2013.iso / Internet / AvantBrowser / asetup.exe / _data / webkit / chrome_100_percent.pak / Unnamed File 000005.txt < prev    next >
Text File  |  2013-04-03  |  18KB  |  507 lines

  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4.  
  5.   var DCHECK = requireNative('logging').DCHECK;
  6.   var eventBindingsNatives = requireNative('event_bindings');
  7.   var AttachEvent = eventBindingsNatives.AttachEvent;
  8.   var DetachEvent = eventBindingsNatives.DetachEvent;
  9.   var AttachFilteredEvent = eventBindingsNatives.AttachFilteredEvent;
  10.   var DetachFilteredEvent = eventBindingsNatives.DetachFilteredEvent;
  11.   var MatchAgainstEventFilter = eventBindingsNatives.MatchAgainstEventFilter;
  12.   var sendRequest = require('sendRequest').sendRequest;
  13.   var utils = require('utils');
  14.   var validate = require('schemaUtils').validate;
  15.  
  16.   var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
  17.   var GetExtensionAPIDefinition =
  18.       requireNative('apiDefinitions').GetExtensionAPIDefinition;
  19.  
  20.   // Schemas for the rule-style functions on the events API that
  21.   // only need to be generated occasionally, so populate them lazily.
  22.   var ruleFunctionSchemas = {
  23.     // These values are set lazily:
  24.     // addRules: {},
  25.     // getRules: {},
  26.     // removeRules: {}
  27.   };
  28.  
  29.   // This function ensures that |ruleFunctionSchemas| is populated.
  30.   function ensureRuleSchemasLoaded() {
  31.     if (ruleFunctionSchemas.addRules)
  32.       return;
  33.     var eventsSchema = GetExtensionAPIDefinition("events")[0];
  34.     var eventType = utils.lookup(eventsSchema.types, 'id', 'events.Event');
  35.  
  36.     ruleFunctionSchemas.addRules =
  37.         utils.lookup(eventType.functions, 'name', 'addRules');
  38.     ruleFunctionSchemas.getRules =
  39.         utils.lookup(eventType.functions, 'name', 'getRules');
  40.     ruleFunctionSchemas.removeRules =
  41.         utils.lookup(eventType.functions, 'name', 'removeRules');
  42.   }
  43.  
  44.   // Local implementation of JSON.parse & JSON.stringify that protect us
  45.   // from being clobbered by an extension.
  46.   //
  47.   // TODO(aa): This makes me so sad. We shouldn't need it, as we can just pass
  48.   // Values directly over IPC without serializing to strings and use
  49.   // JSONValueConverter.
  50.   chromeHidden.JSON = new (function() {
  51.     var $Object = Object;
  52.     var $Array = Array;
  53.     var $jsonStringify = JSON.stringify;
  54.     var $jsonParse = JSON.parse;
  55.  
  56.     this.stringify = function(thing) {
  57.       var customizedObjectToJSON = $Object.prototype.toJSON;
  58.       var customizedArrayToJSON = $Array.prototype.toJSON;
  59.       if (customizedObjectToJSON !== undefined) {
  60.         $Object.prototype.toJSON = null;
  61.       }
  62.       if (customizedArrayToJSON !== undefined) {
  63.         $Array.prototype.toJSON = null;
  64.       }
  65.       try {
  66.         return $jsonStringify(thing);
  67.       } finally {
  68.         if (customizedObjectToJSON !== undefined) {
  69.           $Object.prototype.toJSON = customizedObjectToJSON;
  70.         }
  71.         if (customizedArrayToJSON !== undefined) {
  72.           $Array.prototype.toJSON = customizedArrayToJSON;
  73.         }
  74.       }
  75.     };
  76.  
  77.     this.parse = function(thing) {
  78.       return $jsonParse(thing);
  79.     };
  80.   })();
  81.  
  82.   // A map of event names to the event object that is registered to that name.
  83.   var attachedNamedEvents = {};
  84.  
  85.   // An array of all attached event objects, used for detaching on unload.
  86.   var allAttachedEvents = [];
  87.  
  88.   // A map of functions that massage event arguments before they are dispatched.
  89.   // Key is event name, value is function.
  90.   var eventArgumentMassagers = {};
  91.  
  92.   // Handles adding/removing/dispatching listeners for unfiltered events.
  93.   var UnfilteredAttachmentStrategy = function(event) {
  94.     this.event_ = event;
  95.   };
  96.  
  97.   UnfilteredAttachmentStrategy.prototype.onAddedListener =
  98.       function(listener) {
  99.     // Only attach / detach on the first / last listener removed.
  100.     if (this.event_.listeners_.length == 0)
  101.       AttachEvent(this.event_.eventName_);
  102.   };
  103.  
  104.   UnfilteredAttachmentStrategy.prototype.onRemovedListener =
  105.       function(listener) {
  106.     if (this.event_.listeners_.length == 0)
  107.       this.detach(true);
  108.   };
  109.  
  110.   UnfilteredAttachmentStrategy.prototype.detach = function(manual) {
  111.     DetachEvent(this.event_.eventName_, manual);
  112.   };
  113.  
  114.   UnfilteredAttachmentStrategy.prototype.getListenersByIDs = function(ids) {
  115.     return this.event_.listeners_;
  116.   };
  117.  
  118.   var FilteredAttachmentStrategy = function(event) {
  119.     this.event_ = event;
  120.     this.listenerMap_ = {};
  121.   };
  122.  
  123.   FilteredAttachmentStrategy.idToEventMap = {};
  124.  
  125.   FilteredAttachmentStrategy.prototype.onAddedListener = function(listener) {
  126.     var id = AttachFilteredEvent(this.event_.eventName_,
  127.         listener.filters || {});
  128.     if (id == -1)
  129.       throw new Error("Can't add listener");
  130.     listener.id = id;
  131.     this.listenerMap_[id] = listener;
  132.     FilteredAttachmentStrategy.idToEventMap[id] = this.event_;
  133.   };
  134.  
  135.   FilteredAttachmentStrategy.prototype.onRemovedListener = function(listener) {
  136.     this.detachListener(listener, true);
  137.   };
  138.  
  139.   FilteredAttachmentStrategy.prototype.detachListener =
  140.       function(listener, manual) {
  141.     if (listener.id == undefined)
  142.       throw new Error("listener.id undefined - '" + listener + "'");
  143.     var id = listener.id;
  144.     delete this.listenerMap_[id];
  145.     delete FilteredAttachmentStrategy.idToEventMap[id];
  146.     DetachFilteredEvent(id, manual);
  147.   };
  148.  
  149.   FilteredAttachmentStrategy.prototype.detach = function(manual) {
  150.     for (var i in this.listenerMap_)
  151.       this.detachListener(this.listenerMap_[i], manual);
  152.   };
  153.  
  154.   FilteredAttachmentStrategy.prototype.getListenersByIDs = function(ids) {
  155.     var result = [];
  156.     for (var i = 0; i < ids.length; i++)
  157.       result.push(this.listenerMap_[ids[i]]);
  158.     return result;
  159.   };
  160.  
  161.   chromeHidden.parseEventOptions = function(opt_eventOptions) {
  162.     function merge(dest, src) {
  163.       for (var k in src) {
  164.         if (!dest.hasOwnProperty(k)) {
  165.           dest[k] = src[k];
  166.         }
  167.       }
  168.     }
  169.  
  170.     var options = opt_eventOptions || {};
  171.     merge(options,
  172.         {supportsFilters: false,
  173.          supportsListeners: true,
  174.          supportsRules: false,
  175.         });
  176.     return options;
  177.   };
  178.  
  179.   // Event object.  If opt_eventName is provided, this object represents
  180.   // the unique instance of that named event, and dispatching an event
  181.   // with that name will route through this object's listeners. Note that
  182.   // opt_eventName is required for events that support rules.
  183.   //
  184.   // Example:
  185.   //   chrome.tabs.onChanged = new chrome.Event("tab-changed");
  186.   //   chrome.tabs.onChanged.addListener(function(data) { alert(data); });
  187.   //   chromeHidden.Event.dispatch("tab-changed", "hi");
  188.   // will result in an alert dialog that says 'hi'.
  189.   //
  190.   // If opt_eventOptions exists, it is a dictionary that contains the boolean
  191.   // entries "supportsListeners" and "supportsRules".
  192.   chrome.Event = function(opt_eventName, opt_argSchemas, opt_eventOptions) {
  193.     this.eventName_ = opt_eventName;
  194.     this.listeners_ = [];
  195.     this.eventOptions_ = chromeHidden.parseEventOptions(opt_eventOptions);
  196.  
  197.     if (this.eventOptions_.supportsRules && !opt_eventName)
  198.       throw new Error("Events that support rules require an event name.");
  199.  
  200.     if (this.eventOptions_.supportsFilters) {
  201.       this.attachmentStrategy_ = new FilteredAttachmentStrategy(this);
  202.     } else {
  203.       this.attachmentStrategy_ = new UnfilteredAttachmentStrategy(this);
  204.     }
  205.  
  206.     // Validate event arguments (the data that is passed to the callbacks)
  207.     // if we are in debug.
  208.     if (opt_argSchemas &&
  209.         chromeHidden.validateCallbacks) {
  210.  
  211.       this.validateEventArgs_ = function(args) {
  212.         try {
  213.           validate(args, opt_argSchemas);
  214.         } catch (exception) {
  215.           return "Event validation error during " + opt_eventName + " -- " +
  216.                  exception;
  217.         }
  218.       };
  219.     } else {
  220.       this.validateEventArgs_ = function() {}
  221.     }
  222.   };
  223.  
  224.  
  225.   chromeHidden.Event = {};
  226.  
  227.   // callback is a function(args, dispatch). args are the args we receive from
  228.   // dispatchEvent(), and dispatch is a function(args) that dispatches args to
  229.   // its listeners.
  230.   chromeHidden.Event.registerArgumentMassager = function(name, callback) {
  231.     if (eventArgumentMassagers[name])
  232.       throw new Error("Massager already registered for event: " + name);
  233.     eventArgumentMassagers[name] = callback;
  234.   };
  235.  
  236.   // Dispatches a named event with the given argument array. The args array is
  237.   // the list of arguments that will be sent to the event callback.
  238.   chromeHidden.Event.dispatchEvent = function(name, args, filteringInfo) {
  239.     var listenerIDs = null;
  240.  
  241.     if (filteringInfo)
  242.       listenerIDs = MatchAgainstEventFilter(name, filteringInfo);
  243.  
  244.     var event = attachedNamedEvents[name];
  245.     if (!event)
  246.       return;
  247.  
  248.     var dispatchArgs = function(args) {
  249.       result = event.dispatch_(args, listenerIDs);
  250.       if (result)
  251.         DCHECK(!result.validationErrors, result.validationErrors);
  252.     };
  253.  
  254.     if (eventArgumentMassagers[name])
  255.       eventArgumentMassagers[name](args, dispatchArgs);
  256.     else
  257.       dispatchArgs(args);
  258.   };
  259.  
  260.   // Test if a named event has any listeners.
  261.   chromeHidden.Event.hasListener = function(name) {
  262.     return (attachedNamedEvents[name] &&
  263.             attachedNamedEvents[name].listeners_.length > 0);
  264.   };
  265.  
  266.   // Registers a callback to be called when this event is dispatched.
  267.   chrome.Event.prototype.addListener = function(cb, filters) {
  268.     if (!this.eventOptions_.supportsListeners)
  269.       throw new Error("This event does not support listeners.");
  270.     if (this.eventOptions_.maxListeners &&
  271.         this.getListenerCount() >= this.eventOptions_.maxListeners)
  272.       throw new Error("Too many listeners for " + this.eventName_);
  273.     if (filters) {
  274.       if (!this.eventOptions_.supportsFilters)
  275.         throw new Error("This event does not support filters.");
  276.       if (filters.url && !(filters.url instanceof Array))
  277.         throw new Error("filters.url should be an array");
  278.     }
  279.     var listener = {callback: cb, filters: filters};
  280.     this.attach_(listener);
  281.     this.listeners_.push(listener);
  282.   };
  283.  
  284.   chrome.Event.prototype.attach_ = function(listener) {
  285.     this.attachmentStrategy_.onAddedListener(listener);
  286.     if (this.listeners_.length == 0) {
  287.       allAttachedEvents[allAttachedEvents.length] = this;
  288.       if (!this.eventName_)
  289.         return;
  290.  
  291.       if (attachedNamedEvents[this.eventName_]) {
  292.         throw new Error("chrome.Event '" + this.eventName_ +
  293.                         "' is already attached.");
  294.       }
  295.  
  296.       attachedNamedEvents[this.eventName_] = this;
  297.     }
  298.   };
  299.  
  300.   // Unregisters a callback.
  301.   chrome.Event.prototype.removeListener = function(cb) {
  302.     if (!this.eventOptions_.supportsListeners)
  303.       throw new Error("This event does not support listeners.");
  304.     var idx = this.findListener_(cb);
  305.     if (idx == -1) {
  306.       return;
  307.     }
  308.  
  309.     var removedListener = this.listeners_.splice(idx, 1)[0];
  310.     this.attachmentStrategy_.onRemovedListener(removedListener);
  311.  
  312.     if (this.listeners_.length == 0) {
  313.       var i = allAttachedEvents.indexOf(this);
  314.       if (i >= 0)
  315.         delete allAttachedEvents[i];
  316.       if (!this.eventName_)
  317.         return;
  318.  
  319.       if (!attachedNamedEvents[this.eventName_]) {
  320.         throw new Error("chrome.Event '" + this.eventName_ +
  321.                         "' is not attached.");
  322.       }
  323.  
  324.       delete attachedNamedEvents[this.eventName_];
  325.     }
  326.   };
  327.  
  328.   // Test if the given callback is registered for this event.
  329.   chrome.Event.prototype.hasListener = function(cb) {
  330.     if (!this.eventOptions_.supportsListeners)
  331.       throw new Error("This event does not support listeners.");
  332.     return this.findListener_(cb) > -1;
  333.   };
  334.  
  335.   // Test if any callbacks are registered for this event.
  336.   chrome.Event.prototype.hasListeners = function() {
  337.     return this.getListenerCount() > 0;
  338.   };
  339.  
  340.   // Return the number of listeners on this event.
  341.   chrome.Event.prototype.getListenerCount = function() {
  342.     if (!this.eventOptions_.supportsListeners)
  343.       throw new Error("This event does not support listeners.");
  344.     return this.listeners_.length;
  345.   };
  346.  
  347.   // Returns the index of the given callback if registered, or -1 if not
  348.   // found.
  349.   chrome.Event.prototype.findListener_ = function(cb) {
  350.     for (var i = 0; i < this.listeners_.length; i++) {
  351.       if (this.listeners_[i].callback == cb) {
  352.         return i;
  353.       }
  354.     }
  355.  
  356.     return -1;
  357.   };
  358.  
  359.   chrome.Event.prototype.dispatch_ = function(args, listenerIDs) {
  360.     if (!this.eventOptions_.supportsListeners)
  361.       throw new Error("This event does not support listeners.");
  362.     var validationErrors = this.validateEventArgs_(args);
  363.     if (validationErrors) {
  364.       console.error(validationErrors);
  365.       return {validationErrors: validationErrors};
  366.     }
  367.  
  368.     var listeners = this.attachmentStrategy_.getListenersByIDs(listenerIDs);
  369.  
  370.     var results = [];
  371.     for (var i = 0; i < listeners.length; i++) {
  372.       try {
  373.         var result = this.dispatchToListener(listeners[i].callback, args);
  374.         if (result !== undefined)
  375.           results.push(result);
  376.       } catch (e) {
  377.         console.error("Error in event handler for '" + this.eventName_ +
  378.                       "': " + e.message + ' ' + e.stack);
  379.       }
  380.     }
  381.     if (results.length)
  382.       return {results: results};
  383.   }
  384.  
  385.   // Can be overridden to support custom dispatching.
  386.   chrome.Event.prototype.dispatchToListener = function(callback, args) {
  387.     return callback.apply(null, args);
  388.   }
  389.  
  390.   // Dispatches this event object to all listeners, passing all supplied
  391.   // arguments to this function each listener.
  392.   chrome.Event.prototype.dispatch = function(varargs) {
  393.     return this.dispatch_(Array.prototype.slice.call(arguments), undefined);
  394.   };
  395.  
  396.   // Detaches this event object from its name.
  397.   chrome.Event.prototype.detach_ = function() {
  398.     this.attachmentStrategy_.detach(false);
  399.   };
  400.  
  401.   chrome.Event.prototype.destroy_ = function() {
  402.     this.listeners_ = [];
  403.     this.validateEventArgs_ = [];
  404.     this.detach_(false);
  405.   };
  406.  
  407.   chrome.Event.prototype.addRules = function(rules, opt_cb) {
  408.     if (!this.eventOptions_.supportsRules)
  409.       throw new Error("This event does not support rules.");
  410.  
  411.     // Takes a list of JSON datatype identifiers and returns a schema fragment
  412.     // that verifies that a JSON object corresponds to an array of only these
  413.     // data types.
  414.     function buildArrayOfChoicesSchema(typesList) {
  415.       return {
  416.         'type': 'array',
  417.         'items': {
  418.           'choices': typesList.map(function(el) {return {'$ref': el};})
  419.         }
  420.       };
  421.     };
  422.  
  423.     // Validate conditions and actions against specific schemas of this
  424.     // event object type.
  425.     // |rules| is an array of JSON objects that follow the Rule type of the
  426.     // declarative extension APIs. |conditions| is an array of JSON type
  427.     // identifiers that are allowed to occur in the conditions attribute of each
  428.     // rule. Likewise, |actions| is an array of JSON type identifiers that are
  429.     // allowed to occur in the actions attribute of each rule.
  430.     function validateRules(rules, conditions, actions) {
  431.       var conditionsSchema = buildArrayOfChoicesSchema(conditions);
  432.       var actionsSchema = buildArrayOfChoicesSchema(actions);
  433.       rules.forEach(function(rule) {
  434.         validate([rule.conditions], [conditionsSchema]);
  435.         validate([rule.actions], [actionsSchema]);
  436.       })
  437.     };
  438.  
  439.     if (!this.eventOptions_.conditions || !this.eventOptions_.actions) {
  440.       throw new Error('Event ' + this.eventName_ + ' misses conditions or ' +
  441.                 'actions in the API specification.');
  442.     }
  443.  
  444.     validateRules(rules,
  445.                   this.eventOptions_.conditions,
  446.                   this.eventOptions_.actions);
  447.  
  448.     ensureRuleSchemasLoaded();
  449.     // We remove the first parameter from the validation to give the user more
  450.     // meaningful error messages.
  451.     validate([rules, opt_cb],
  452.              ruleFunctionSchemas.addRules.parameters.slice().splice(1));
  453.     sendRequest("events.addRules", [this.eventName_, rules, opt_cb],
  454.                 ruleFunctionSchemas.addRules.parameters);
  455.   }
  456.  
  457.   chrome.Event.prototype.removeRules = function(ruleIdentifiers, opt_cb) {
  458.     if (!this.eventOptions_.supportsRules)
  459.       throw new Error("This event does not support rules.");
  460.     ensureRuleSchemasLoaded();
  461.     // We remove the first parameter from the validation to give the user more
  462.     // meaningful error messages.
  463.     validate([ruleIdentifiers, opt_cb],
  464.              ruleFunctionSchemas.removeRules.parameters.slice().splice(1));
  465.     sendRequest("events.removeRules",
  466.                 [this.eventName_, ruleIdentifiers, opt_cb],
  467.                 ruleFunctionSchemas.removeRules.parameters);
  468.   }
  469.  
  470.   chrome.Event.prototype.getRules = function(ruleIdentifiers, cb) {
  471.     if (!this.eventOptions_.supportsRules)
  472.       throw new Error("This event does not support rules.");
  473.     ensureRuleSchemasLoaded();
  474.     // We remove the first parameter from the validation to give the user more
  475.     // meaningful error messages.
  476.     validate([ruleIdentifiers, cb],
  477.              ruleFunctionSchemas.getRules.parameters.slice().splice(1));
  478.  
  479.     sendRequest("events.getRules",
  480.                 [this.eventName_, ruleIdentifiers, cb],
  481.                 ruleFunctionSchemas.getRules.parameters);
  482.   }
  483.  
  484.   // Special load events: we don't use the DOM unload because that slows
  485.   // down tab shutdown.  On the other hand, onUnload might not always fire,
  486.   // since Chrome will terminate renderers on shutdown (SuddenTermination).
  487.   chromeHidden.onLoad = new chrome.Event();
  488.   chromeHidden.onUnload = new chrome.Event();
  489.  
  490.   chromeHidden.dispatchOnLoad =
  491.       chromeHidden.onLoad.dispatch.bind(chromeHidden.onLoad);
  492.  
  493.   chromeHidden.dispatchOnUnload = function() {
  494.     chromeHidden.onUnload.dispatch();
  495.     for (var i = 0; i < allAttachedEvents.length; ++i) {
  496.       var event = allAttachedEvents[i];
  497.       if (event)
  498.         event.detach_();
  499.     }
  500.   };
  501.  
  502.   chromeHidden.dispatchError = function(msg) {
  503.     console.error(msg);
  504.   };
  505.  
  506.   exports.Event = chrome.Event;
  507.